//
// Created by yunnan xu on 2020/11/17.
//
#define STB_IMAGE_IMPLEMENTATION

#include "Texture.h"
#include "stb_image.h"
#include "ResourceMgr.h"
#include "EngineCore.h"
#include "GLESUtils.h"
#include "CPPUtils.h"
#include "CompressTextures.h"

void Texture::LoadFromFile(const char * file_path)
{
    long file_size = ResourceMgr::GetInstance()->GetFileLength(file_path, AASSET_MODE_BUFFER);
    texFileContent = new unsigned char [file_size];

    int read_res = ResourceMgr::GetInstance()->ReadFile(file_path, texFileContent, file_size, AASSET_MODE_BUFFER);
    if (read_res < 0)
    {
        LOGE("Texture2D ReadFile error filepath = %s, res = %d", file_path, read_res);
        return;
    }
    else {
        LOGI("Texture2D filepath = %s, read_res = %d", file_path, read_res);
    }

    if (StringEndsWith(file_path, ".astc"))
    {
        ASTC_HEADER * astc_header_ptr = (ASTC_HEADER *)  texFileContent;

        /* Merge x,y,z-sizes from 3 chars into one integer value. */
        int xsize = astc_header_ptr->xsize[0] + (astc_header_ptr->xsize[1] << 8) + (astc_header_ptr->xsize[2] << 16);
        int ysize = astc_header_ptr->ysize[0] + (astc_header_ptr->ysize[1] << 8) + (astc_header_ptr->ysize[2] << 16);
        int zsize = astc_header_ptr->zsize[0] + (astc_header_ptr->zsize[1] << 8) + (astc_header_ptr->zsize[2] << 16);

        /* Compute number of blocks in each direction. */
        int xblocks = (xsize + astc_header_ptr->blockdim_x - 1) / astc_header_ptr->blockdim_x;
        int yblocks = (ysize + astc_header_ptr->blockdim_y - 1) / astc_header_ptr->blockdim_y;
        int zblocks = (zsize + astc_header_ptr->blockdim_z - 1) / astc_header_ptr->blockdim_z;

        /* Each block is encoded on 16 bytes, so calculate total compressed image data size. */
        n_bytes_to_read = xblocks * yblocks * zblocks << 4;

        tex_type = TextureType::ASTC;

        width = xsize;
        height = ysize;
        deep = zsize;

        if (astc_header_ptr->blockdim_x == 4 && astc_header_ptr->blockdim_y == 4)
        {
            internal_tex_type = GL_COMPRESSED_RGBA_ASTC_4x4;
        }
        else if (astc_header_ptr->blockdim_x == 5 && astc_header_ptr->blockdim_y == 4)
        {
            internal_tex_type = GL_COMPRESSED_RGBA_ASTC_5x4;
        }
        else if (astc_header_ptr->blockdim_x == 5 && astc_header_ptr->blockdim_y == 5)
        {
            internal_tex_type = GL_COMPRESSED_RGBA_ASTC_5x5;
        }
        else if (astc_header_ptr->blockdim_x == 6 && astc_header_ptr->blockdim_y == 5)
        {
            internal_tex_type = GL_COMPRESSED_RGBA_ASTC_6x5;
        }
        else if (astc_header_ptr->blockdim_x == 6 && astc_header_ptr->blockdim_y == 6)
        {
            internal_tex_type = GL_COMPRESSED_RGBA_ASTC_6x6;
        }

        texRGBContent = texFileContent;

        LOGI("ASTC_HEADER %d, %d, %d, %d, %d, %d， %d", xsize, ysize, zsize, astc_header_ptr->blockdim_x, astc_header_ptr->blockdim_y, astc_header_ptr->blockdim_z, n_bytes_to_read);
    }
    else if (StringEndsWith(file_path, ".etc"))
    {
        tex_type = TextureType::ETC;
    }
    else
    {
        /*
         *  函数原型：STBIDEF stbi_uc *stbi_load_from_memory   (stbi_uc const *buffer, int len   , int *x, int *y, int *channels_in_file, int desired_channels);
         * */

        texRGBContent = stbi_load_from_memory((const unsigned char *) texFileContent, read_res, &width, &height, &channels, 0);
        LOGI("Texture2D %s, x = %d, y = %d, channels_in_file = %d", file_path, width, height, channels);

        tex_type = TextureType::RAW;
    }
}


Texture2D<GraphicsAPI::GLESv3>::Texture2D(const char * file_path, int texture_unit)
{
    tex_unit_index = texture_unit + GL_TEXTURE0;

    LoadFromFile(file_path);

    GL_CHECK(glGenTextures(1, &texture));
    GL_CHECK(glActiveTexture(tex_unit_index));
    GL_CHECK(glBindTexture(GL_TEXTURE_2D, texture));

    /*
     * 函数原型 glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
     * */

    if (tex_type == TextureType::RAW) {

        GLenum format = GL_RGBA;
        GLint internal_format = GL_RGBA;

        if (channels == 3)
        {
            format = GL_RGB;
            internal_format = GL_RGB;
        }
        else if (channels == 4)
        {
            format = GL_RGBA;
            internal_format = GL_RGBA;
        }
        else
        {
            assert(false);
        }

        GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, texRGBContent));
        GL_CHECK(glGenerateMipmap(GL_TEXTURE_2D));
    }
    else if (tex_type == TextureType::ASTC)
    {
        GL_CHECK(glCompressedTexImage2D(GL_TEXTURE_2D,
                                        0,
                                        GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6,
                                        width,
                                        height,
                                        0,
                                        n_bytes_to_read,
                                        (const GLvoid*) texRGBContent));

        GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
        GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
        GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_REPEAT));
        GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_REPEAT));
        LOGI("ASTC Reach here ... ");
    }
    else if (tex_type == TextureType::ETC)
    {

    }
}

Texture2D<GraphicsAPI::GLESv3>::Texture2D(GLenum internalformat, int levels, GLsizei width, GLsizei height, const std::unordered_map<int, int> * texture_2d_params, int texture_unit)
{
    tex_unit_index = texture_unit + GL_TEXTURE0;
    GL_CHECK(glGenTextures(1, &texture));
    GL_CHECK(glActiveTexture(tex_unit_index));
    GL_CHECK(glBindTexture(GL_TEXTURE_2D, texture));

    GL_CHECK(glTexStorage2D(GL_TEXTURE_2D,
                   levels,
                   internalformat,
                   width,
                   height));

    if (texture_2d_params) {
        for (auto &kv : *texture_2d_params) {
            GL_CHECK(glTexParameteri(GL_TEXTURE_2D,
                            kv.first,
                            kv.second));
        }
    }
}


CubeMapTexture<GraphicsAPI::GLESv3>::CubeMapTexture(std::vector<const char *> cube_map_file_path_list, int texture_unit)
{
    tex_unit_index = texture_unit + GL_TEXTURE0;
    GL_CHECK(glGenTextures(1, &texture));
    GL_CHECK(glActiveTexture(tex_unit_index));
    GL_CHECK(glBindTexture(GL_TEXTURE_CUBE_MAP, texture));

    for (unsigned int i = 0; i < cube_map_file_path_list.size(); i++)
    {
        // unsigned char *data = stbi_load(cube_map_file_path_list[i], &width, &height, &nrChannels, 0);
        LoadFromFile(cube_map_file_path_list[i]);

        if (texRGBContent)
        {
            GL_CHECK(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
                         0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texRGBContent
            ));
            stbi_image_free(texRGBContent);
        }
        else
        {
            stbi_image_free(texRGBContent);
        }
    }

    GL_CHECK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GL_CHECK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GL_CHECK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GL_CHECK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
    GL_CHECK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE));
}


